#include "L_System.h"
#include <time.h>
#include <stdlib.h>

char const *symbolList[] = {"F", "1", "2", "3", "4", "5"};

	L_System :: L_System()
	{
		edgeLength = 0;
		theta = 0;
		returnList = std::string();
		buildList = std::string();							
		production = std::string();							 
	}

	L_System :: L_System(std::string i, std::string p,
								float eL, float t)			 
	{														
		returnList = i;										
		production = p;
		edgeLength = eL;									
		theta = t;
	}

	L_System :: L_System(std::string i, std::vector<std::string> productions,
								float eL, float t)			 
	{														
		returnList = i;										
		edgeLength = eL;									
		theta = t;

		//symbolList = {symbols, sym};

		productionList = productions;
	}														
															
	void L_System :: propogate(int generations)				
	{		
		//std::cout << "\nRecursion list: " + returnList << std::endl;
		
		if(generations > 0)									
		{													
			buildList = std::string();						

			for(std::string::iterator it = returnList.begin(); it != returnList.end(); it++)
			{
				if(productionList.size() != 0)
				{
					int j = 0;
					int productionFound = 0;
					for(int i = 0; i < productionList.size(); i++)
					{
						if(*it == *symbolList[i])
						{
							productionFound = 1;
							j = i;
							i = productionList.size();
						}											
					}

					if(productionFound)
					{
						//std::cout << "Production found: " << *symbolList[j] << std::endl;
						//std::cout << "Applying production: " + productionList[j] << std::endl;
						buildList.append(productionList[j]);
						
					}
					else
						buildList.append(1, *it);

				}
				else
				{
					if(*it == 'F')
						buildList.append(production);
					else
						buildList.append(1, *it);
				}
			}

			returnList = buildList;							//Always make the base list
			propogate(generations - 1);						//into the newly generated
		}													//list after each recursion
		else
		{
			return;
		}
	}

	Stochastic_L_System :: Stochastic_L_System(std::string i, edgeProduction *p,
								float eL, float t)			 
	{														
		returnList = i;										
		production = p;
		edgeLength = eL;
		theta = t;
	}

	Stochastic_L_System :: Stochastic_L_System(std::string i, std::vector<edgeProduction> productions,
												float eL, float t)
	{
		returnList = i;
		theta = t;
		edgeLength = eL;
		productionList = productions;
	}

	void Stochastic_L_System :: propogate(int generations)
	{
		srand(time(NULL));
		if(generations > 0)									
		{													
			buildList = std::string();						

			for(std::string::iterator it = returnList.begin(); it != returnList.end(); it++)
			{
				if(productionList.size() != 0)
				{
					int j = 0;
					int productionFound = 0;
					for(int i = 0; i < productionList.size(); i++)
					{
						if(*it == *symbolList[i])
						{
							productionFound = 1;
							j = i;
							i = productionList.size();
						}											
					}

					if(productionFound)
					{
						srand(time(NULL));
						edgeProduction eP = productionList[j];
						float sum = 0;
						float random = (rand() % 100) / 100;
						int i = -1;
						while(sum < random)
						{
							i++;
							sum += eP.weights[i];
						}
						buildList.append(eP.productions[i]);
					}
					else
						buildList.append(1, *it);

				}
				else
				{
					if(*it == 'F')
					{
						float sum = 0;
						float random = ((float)(rand() % 99))/100;
						int i = -1;
						while(sum <= random)							//Not generating random number
						{											//Not entering loop
							i++;									
							sum += production->weights[i];
						}
						buildList.append(production->productions[i]);	//Indexing with -1 causes error
					}
					else
						buildList.append(1, *it);
				}
			}

			returnList = buildList;							//Always make the base list
			propogate(generations - 1);						//into the newly generated
		}													//list after each recursion
		else
		{
			return;
		}
	}

	Parametric_L_System :: Parametric_L_System()
	{
		returnList = std::vector<Module>();
		buildList = std::vector<Module>();

		edgeLength = 0;
		theta = 0;

		productionList = std::vector<Production>();
	}

	Parametric_L_System :: Parametric_L_System(float eL, float t, std::vector<Module> axiom, std::vector<Production> prodList)
	{
		edgeLength = eL;
		theta = t;

		returnList = axiom;
		productionList = prodList;
	};

	void Parametric_L_System :: propogate(int generations)
	{
		if(generations > 0)									
		{													
			buildList = std::vector<Module>();	
			std::vector<Module> application;
			bool productionFound = false;

			for(auto it : returnList)				//For every module in the list under analysis
			{
				productionFound = false;
				for(int i = 0; i < productionList.size(); i++)		//Check every production in the production list
				{													//Implicitly it is assumed that one production is possible per module
					if(productionList[i].checkCondition(it))		//per iteration based on production count or condition boundaries
					{
						application = productionList[i].applyProduction(it);
						buildList.insert(buildList.end(), application.begin(), application.end());
						productionFound = true;
					}
				}
				if(!productionFound)
					buildList.push_back(it);					//If there are no valid productions just append the current module as-is
			}
			returnList = buildList;							//Always make the base list
			propogate(generations - 1);						//into the newly generated
		}													//list after each recursion
		else
		{
			return;
		}
	}